using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.IO;

namespace AutomaticLogin
{
    class AutoLoginManager
    {
        private HtmlDocument m_Document = null;
        private WebBrowser m_Browser = null;
        private static List<string> s_NonInputs = new List<string>();
        private List<HtmlElement> m_Inputs = null;
        private string m_Url = null;
        private HtmlElement m_Psw = null;
        private LoginInfos m_LoginInfos = null;

        #region Constructors

        // static CTOR
        // initialize all non input types
        static AutoLoginManager()
        {
            s_NonInputs.Add("button");
            s_NonInputs.Add("hidden");
            s_NonInputs.Add("image");
            s_NonInputs.Add("reset");
            s_NonInputs.Add("submit");
            s_NonInputs.Add("checkbox");
            s_NonInputs.Add("file");
            s_NonInputs.Add("radio");
        }
                
        //CTOR
        public AutoLoginManager(WebBrowser browser, string url, LoginInfos loginInfos)
        {
            // return if login information is not provided
            if (loginInfos == null) return;

            m_LoginInfos = loginInfos;

            m_Url = url;
            m_Inputs = new List<HtmlElement>();
            m_Document = browser.Document;
            m_Browser = browser;
            // check if url have save login information
            if (m_LoginInfos[url] != null)
            {
                //embed login info in page
                m_Psw = GetPassword();
                EmbedInfo();
            }
        }

        #endregion

        /// <summary>
        /// check if page contain login fields and register for it's 
        /// navigation event
        /// </summary>
        public void RegisterInfoCollection()
        {
            if (GetPassword() == null)
            {
                MessageBox.Show("Not recognised as login form !");
                return;
            }
            else
            {
                MessageBox.Show("Recording login information of form !");
            }
            m_Browser.Navigating += new WebBrowserNavigatingEventHandler(browser_Navigating);
        }

        #region Private Methods

        private void browser_Navigating(object sender, WebBrowserNavigatingEventArgs e)
        {
            WebBrowser wb = (WebBrowser)sender;
            wb.Navigating -= browser_Navigating;

            // save the info
            LoginInfo values = new LoginInfo();
            values.key = m_Url;
            values.LoginInputs = new LoginInputs();
            //(m_Url, values);
            m_Psw = GetPassword();
            if (m_Psw != null)
            {
                GetInputs();
            }
            // save the login values for the url
            foreach (HtmlElement elem in m_Inputs)
            {
                string val = elem.GetAttribute("value");
                string key = elem.GetAttribute("type") + elem.GetAttribute("id") + elem.GetAttribute("name");
                values.LoginInputs[key] = val;
            }

            m_LoginInfos[m_Url] = values;

        }
        
        /// <summary>
        /// search for password in main document but also in IFrames
        /// </summary>
        /// <returns></returns>
        private HtmlElement GetPassword()
        {
            HtmlElement password = GetPassword(m_Document);
            if (password == null)
            {
                foreach (HtmlWindow frameWindow in m_Document.Window.Frames)
                {
                    try
                    {
                        password = GetPassword(frameWindow.Document);
                        if (password != null) return password;
                    }
                    catch { }
                }
            }

            return password;
        }
        
        /// <summary>
        /// look for password element in the document by searching
        /// input element of type "password"
        /// </summary>
        /// <param name="doc"></param>
        /// <returns></returns>
        private HtmlElement GetPassword(HtmlDocument doc)
        {
            if (doc == null)
                return null;


            foreach (HtmlElement elem in doc.GetElementsByTagName("input"))
            {
                string type = elem.GetAttribute("type");
                if (!string.IsNullOrEmpty(type) && type.ToLower() == "password")
                    return elem;
            }

            return null; // not found
        }

        /// <summary>
        /// collect the inputs from the Form
        /// </summary>
        private void GetInputs()
        {
            // get the Form element
            HtmlElement form = GetForm();
            foreach (HtmlElement elem in form.GetElementsByTagName("input"))
            {
                string type = elem.GetAttribute("type").ToLower();
                // skip all non inputs
                if (s_NonInputs.Contains(type))
                    continue;
                m_Inputs.Add(elem);
            }
        }

        /// <summary>
        /// embedd saved values to to form
        /// </summary>
        private void EmbedInfo()
        {
            HtmlElement form = GetForm();
            foreach (HtmlElement elem in form.GetElementsByTagName("input"))
            {
                string type = elem.GetAttribute("type").ToLower();
                // skip non input types
                if (s_NonInputs.Contains(type))
                    continue;
                // build the key of the element
                string key = elem.GetAttribute("type") + elem.GetAttribute("id") + elem.GetAttribute("name");
                elem.SetAttribute("value", m_LoginInfos[m_Url].LoginInputs[key]);
            }
            return;
        }

        private HtmlElement GetForm()
        {
            // after the password is found going up the hierarchy chain until
            // Form element is found
            HtmlElement form = m_Psw.Parent;
            while (form.TagName.ToLower() != "form")
                form = form.Parent;
            return form;
        }

        #endregion
    }
}
